<?php

namespace Xin\LaravelFortify\Model;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\Eloquent\Relations\Relation as EloquentRelation;
use Illuminate\Support\Collection;

class Relation
{
	/**
	 * @param string $targetType
	 * @param int $targetId
	 * @param array $filterAttributes
	 * @return Model
	 */
	public static function firstOrFail(string $targetType, int $targetId, array $filterAttributes = []): Model
	{
		/** @var Model $targetClass */
		$targetClass = EloquentRelation::getMorphedModel($targetType);
		if (empty($targetClass)) {
			throw new \RuntimeException("{$targetType} not defined morphed alias.");
		}

		if (method_exists($targetClass, '__find')) {
			$info = call_user_func([$targetClass, '__find'], $targetId, $targetType);
			if (empty($info)) {
				throw (new ModelNotFoundException())->setModel($targetClass);
			}

			return $info;
		} else {
			return $targetClass::query()->where(array_merge([
				'id' => $targetId,
			], $filterAttributes))->firstOrFail();
		}
	}

	/**
	 * @param string $targetType
	 * @param int $targetId
	 * @param array $filterAttributes
	 * @return Builder|Model
	 */
	public static function first($targetType, $targetId, $filterAttributes = [])
	{
		/** @var Model $targetClass */
		$targetClass = EloquentRelation::getMorphedModel($targetType);
		if (empty($targetClass)) {
			throw new \RuntimeException("{$targetType} not defined morphed alias.");
		}

		if (method_exists($targetClass, '__find')) {
			return call_user_func([$targetClass, '__find']);
		} else {
			return $targetClass::query()->where(array_merge([
				'id' => $targetId,
			], $filterAttributes))->first();
		}
	}

	/**
	 * 调用关联模型方法
	 * @param string $targetType
	 * @param string $method
	 * @param array $parameters
	 * @return mixed
	 */
	public static function call($targetType, $method, array $parameters = [])
	{
		$targetClass = EloquentRelation::getMorphedModel($targetType);
		if (empty($targetClass)) {
			throw new \RuntimeException("{$targetType} not defined morphed alias.");
		}

		if (!method_exists($targetClass, $method)) {
			return null;
		}

		return app()->call([$targetClass, $method], $parameters, false);
	}

	/**
	 * 处理多态关联默认处理
	 * @param array $topics
	 * @param callable|null $callback
	 * @return array
	 */
	public static function morphToConstrain(array $topics, callable $callback = null)
	{
		$result = [];
		foreach ($topics as $topicType) {
			$morphToClass = EloquentRelation::getMorphedModel($topicType);
			$result[$morphToClass] = function (Builder $query) use ($callback) {
				if (method_exists($query->getModel(), 'scopeSimple')) {
					$query = $query->simple();
				}

				$callback && $callback($query);
			};
		}

		return $result;
	}

	/**
	 * 取出Pivot数据
	 * @param Collection $result
	 * @param callable $eachCallback
	 * @return Collection
	 */
	public static function carryPivots(Collection $result, callable $eachCallback)
	{
		return $result->each(function (Model $model) use ($eachCallback) {
			$eachCallback(static::carryPivot($model), $model);
		});
	}

	/**
	 * 取出Pivot数据
	 * @param Model $model
	 * @return array
	 */
	public static function carryPivot(Model $model)
	{
		$pivot = [];

		foreach ($model->getData() as $key => $val) {
			if (strpos($key, '__')) {
				[$name, $attr] = explode('__', $key, 2);

				if ('pivot' == $name) {
					$pivot[$attr] = $val;
					unset($model->$key);
				}
			}
		}

		return $pivot;
	}

	/**
	 * 获取 “belongs to” 关系名称。
	 *
	 * @param $relation
	 * @return string
	 */
	public static function guessBelongsToName($relation): string
	{
		if (!empty($relation)) {
			return $relation;
		}

		[$one, $two, $caller] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);

		return $caller['function'];
	}

}
